﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace WeChatImageDatEncryption
{
    class Program
    {
        //JPEG (jpg)，文件头：FFD8FFE1
        //PNG (png)，文件头：89504E47 
        static List<FileHeadInfo> ImageFileHeads = new List<FileHeadInfo>() {
            new FileHeadInfo(){
            FileHeadByte=0xff,
            CheckBytes=new byte[]{ 0xff,0xd8},
            Remarks="JPEG (jpg)，文件头：FFD8FFE1",
            Suffix=".jpg",
            Key=-1
            },
             new FileHeadInfo(){
            FileHeadByte=0x89,
            CheckBytes=new byte[]{ 0x89, 0x50},
            Remarks="PNG (png)，文件头：89504E47",
            Suffix=".png",
            Key=-1,
            },
        };

        static Dictionary<int, FileHeadInfo> LocalFileMap = new Dictionary<int, FileHeadInfo>();
        static void Main(string[] args)
        {
            Console.ForegroundColor = ConsoleColor.White;
            while (true)
            {
                try
                {
                    Console.Write("请选择操作(1：解密:2：加密,3：获取加密Key)：");
                    string opType = Console.ReadLine();
                    switch (opType)
                    {
                        case "1":
                            Decrypt();
                            break;
                        case "2":
                            Encryption();
                            break;
                        case "3":
                            GetKey();
                            break;
                        default:
                            throw new Exception("未知操作类型");
                    }
                }
                catch (Exception ex)
                {
                    WriteLine(ex.Message, ConsoleColor.Red);
                }
            }
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="dirPath"></param>
        static void DEFile(string dirPath)
        {
            string[] filePaths = Directory.GetFiles(dirPath, "*.dat");
            if (filePaths == null || filePaths.Length == 0)
            {
                throw new Exception("该目录下不存在dat文件");
            }

            byte[] orfile = null;
            byte[] enbyte = null;
            string enDir = AppDomain.CurrentDomain.BaseDirectory + "/DecryptData";
            if (!Directory.Exists(enDir))
            {
                Directory.CreateDirectory(enDir);
            }

            FileHeadInfo key = null;
            foreach (string item in filePaths)
            {
                try
                {
                    orfile = File.ReadAllBytes(item);
                    if (isEncryption(orfile))
                    {
                        key = GetKey(item);
                        if (key == null)
                        {
                            Console.ForegroundColor = ConsoleColor.Red;
                            WriteLine($"{item},解密失败", ConsoleColor.Red);
                            Console.ForegroundColor = ConsoleColor.White;
                            continue;
                        }
                        enbyte = EncryptionUtil.EnDeFile(orfile, key.Key);
                        File.WriteAllBytes(enDir + "/" + new FileInfo(item).Name.Replace(".dat", key.Suffix), enbyte);
                    }
                    else
                    {
                        File.WriteAllBytes(enDir + "/" + new FileInfo(item).Name.Replace(".dat", ".jpg"), orfile);
                    }

                    WriteLine(string.Format("{0}处理完成...", item), ConsoleColor.Green);
                }
                catch (Exception ex)
                {

                    Console.WriteLine(string.Format("【{0}】文件处理异常,{1}", item, ex.Message));
                }
            }

            PrintKeyInfo();
            WriteLine("所有文件已处理完成", ConsoleColor.Green);
        }

        /// <summary>
        /// 是否加密
        /// </summary>
        /// <param name="buffer"></param>
        /// <returns></returns>
        static bool isEncryption(byte[] buffer)
        {
            foreach (var item in ImageFileHeads)
            {
                if (buffer[0] == item.CheckBytes[0] && buffer[1] == item.CheckBytes[1])
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// 校验路径
        /// </summary>
        /// <returns></returns>
        static void CheckDirPath(string dirPath)
        {
            if (!Directory.Exists(dirPath))
            {
                throw new Exception("不存在此目录！");
            }
        }

        /// <summary>
        /// 校验路径
        /// </summary>
        /// <returns></returns>
        static void CheckFilePath(string filePath)
        {
            if (!File.Exists(filePath))
            {
                throw new Exception("不存在此文件！");
            }
        }

        /// <summary>
        /// 获取key
        /// </summary>
        /// <returns></returns>
        static FileHeadInfo GetKey(string filePath)
        {
            if (!File.Exists(filePath))
            {
                throw new Exception("不存在此文件：" + filePath);
            }

            byte[] fileBuffer = File.ReadAllBytes(filePath);
            if (LocalFileMap.ContainsKey(fileBuffer[0]))
            {
                return LocalFileMap[fileBuffer[0]];
            }

            FileHeadInfo key = null;
            foreach (var item in ImageFileHeads)
            {
                int tempKey = fileBuffer[0] ^ item.FileHeadByte;
                byte[] enbyte = EncryptionUtil.EnDeFile(fileBuffer, tempKey);
                if (enbyte[0] == item.CheckBytes[0] && enbyte[1] == item.CheckBytes[1])
                {
                    key = item.Clone();
                    key.Key = tempKey;
                    LocalFileMap.Add(fileBuffer[0], key);
                    break;
                }
            }

            return key;
        }

        /// <summary>
        /// 解密
        /// </summary>
        static void Decrypt()
        {
            DEFile(GetInPutDirPath());
        }

        /// <summary>
        /// 加密
        /// </summary>
        static void Encryption()
        {
            Console.Write("请输入加密Key：");
            string keyStr = Console.ReadLine();
            if (string.IsNullOrEmpty(keyStr))
            {
                throw new Exception("请输入加密Key！");
            }

            int key;
            if (!int.TryParse(keyStr, out key))
            {
                throw new Exception("无效的加密Key！");
            }

            Encryption(GetInPutDirPath(), key);
        }

        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="dirPath"></param>
        /// <param name="key"></param>
        static void Encryption(string dirPath, int key)
        {
            string[] filePaths = Directory.GetFiles(dirPath);
            if (filePaths == null || filePaths.Length == 0)
            {
                throw new Exception("该目录下不存在任何文件");
            }

            byte[] orfile = null;
            byte[] enbyte = null;
            string enDir = AppDomain.CurrentDomain.BaseDirectory + "/EncryptionData";
            if (!Directory.Exists(enDir))
            {
                Directory.CreateDirectory(enDir);
            }

            foreach (string item in filePaths)
            {
                try
                {
                    orfile = File.ReadAllBytes(item);
                    enbyte = EncryptionUtil.EnDeFile(orfile, key);
                    FileInfo fileInfo = new FileInfo(item);

                    File.WriteAllBytes(enDir + "/" + fileInfo.Name.Replace(fileInfo.Extension, ".dat"), enbyte);
                    WriteLine(string.Format("{0}处理完成...", item), ConsoleColor.Green);
                }
                catch (Exception ex)
                {

                    WriteLine(string.Format("【{0}】文件处理异常,{1}", item, ex.Message), ConsoleColor.Red);
                }
            }

            WriteLine("所有文件已处理完成", ConsoleColor.Green);
        }

        /// <summary>
        /// 获取加密Key
        /// </summary>
        static void GetKey()
        {
            string filePath = GetInPutFilePath();
            FileHeadInfo key = GetKey(filePath);
            if (key == null)
            {
                WriteLine($"{filePath},获取key失败，非png或jpg文件", ConsoleColor.Red);
            }

            WriteLine($"{filePath},{key.Remarks},加密Key：{key.Key}", ConsoleColor.Yellow);
        }

        /// <summary>
        ///  获取输入的文件目录
        /// </summary>
        /// <returns></returns>
        static string GetInPutDirPath()
        {

            Console.Write("请输入文件目录：");
            string dirPath = Console.ReadLine();
            if (string.IsNullOrEmpty(dirPath))
            {
                throw new Exception("请输入文件目录！");
            }

            CheckDirPath(dirPath);

            return dirPath;
        }

        /// <summary>
        ///  获取输入的文件路径
        /// </summary>
        /// <returns></returns>
        static string GetInPutFilePath()
        {

            Console.Write("请输入文件路径：");
            string filePath = Console.ReadLine();
            if (string.IsNullOrEmpty(filePath))
            {
                throw new Exception("请输入文件路径！");
            }

            CheckFilePath(filePath);

            return filePath;
        }

        static void WriteLine(string msg, ConsoleColor color, ConsoleColor defaultColor = ConsoleColor.White)
        {
            Console.ForegroundColor = color;
            Console.WriteLine(msg);
            Console.ForegroundColor = defaultColor;
        }

        static void PrintKeyInfo()
        {
            if (LocalFileMap != null && LocalFileMap.Count > 0)
            {

                WriteLine($"本机加密Key：{LocalFileMap.FirstOrDefault().Value.Key}", ConsoleColor.Yellow);
            }
        }
    }

    class FileHeadInfo
    {

        /// <summary>
        /// 文件第一个字节
        /// </summary>
        public int FileHeadByte { get; set; }

        /// <summary>
        /// 用于校验解密后文件的2个字节
        /// </summary>
        public byte[] CheckBytes { get; set; }

        /// <summary>
        /// 文件后缀
        /// </summary>
        public string Suffix { get; set; }

        /// <summary>
        /// 备注
        /// </summary>
        public string Remarks { get; set; }

        /// <summary>
        /// 本机加密key
        /// </summary>
        public int Key { get; set; }

        public FileHeadInfo Clone()
        {
            return (FileHeadInfo)this.MemberwiseClone();
        }
    }
}
